package fr.cephb.operonj.tools.cmdline.remapper;

import java.util.ArrayList;
import java.util.List;

import com.sleepycat.bind.tuple.StringBinding;
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.bind.tuple.TupleInput;
import com.sleepycat.bind.tuple.TupleOutput;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.Transaction;

import fr.cephb.operonj.core.je.DuplicateDB;

public class BowtieHit
	{
	public static class CandidatePair
		implements Comparable<CandidatePair>
		{
		private int flanking_length;
		BowtieHit L;
		BowtieHit R;
		
		CandidatePair(BowtieHit L,BowtieHit R,int flanking_length)
			{
			this.R=R;
			this.L=L;
			this.flanking_length=flanking_length;
			}
		
		public int countMismatches()
			{
			return L.mismatches.size()+R.mismatches.size();
			}
		
		@Override
		public boolean equals(Object o)
			{
			if(o==this) return true;
			if(o==null || !(o instanceof CandidatePair)) return false;
			CandidatePair cp=CandidatePair.class.cast(o);
			return R.equals(cp.R) && L.equals(cp.L);
			}
		
		@Override
		public int hashCode()
			{
			return R.hashCode()+31*L.hashCode();
			}
		
		@Override
		public int compareTo(CandidatePair o)
			{
			return countMismatches()-o.countMismatches();
			}
		@Override
		public String toString()
			{
			return " L :"+L+"\n R :"+R;
			}
		
		public int getChromId()
			{
			if(R.chromId!=L.chromId) throw new RuntimeException("getChromId:"+toString());
			return this.R.chromId;
			}
		
		public byte getStrand()
			{
			assert(R.orientation==L.orientation);
			return this.R.orientation;
			}
		
		public int getSNPStart()
			{
			return getStrand()=='+'
				?L.position+ this.flanking_length
				:R.position+ this.flanking_length
				;
				
			}
		
		public int getSNPEnd()
			{
			return getStrand()=='+'
				?R.position
				:L.position
				;
				
			}
		
		}
	
	/**
	 * A mismatch
	 * @author pierre
	 *
	 */
	public static class Mismatch
		{
		public int position;
		public byte query;
		public byte object;
        @Override
        public String toString()
    		{
            return ""+position+":"+(char)query+">"+(char)object;
            }
		}
	
	
	public static class Binding
	extends TupleBinding<BowtieHit>
			{
	        @Override
	        public BowtieHit entryToObject(TupleInput in)
                {
                BowtieHit hit= new BowtieHit();
                hit.readName= in.readString();
                hit.orientation= in.readByte();
                hit.chromId =in.readInt();
                hit.position=in.readInt();
                int n= in.readInt();
                for(int i=0;i< n;++i)
                        {
                        Mismatch mism=new Mismatch();
                        mism.position=in.readInt();
                        mism.query= in.readByte();
                        mism.object=in.readByte();
                        hit.mismatches.add(mism);
                        }
                return hit;
                }

	        @Override
	        public void objectToEntry(BowtieHit hit, TupleOutput out)
                {
                out.writeString(hit.readName);
                out.writeByte(hit.orientation);
                out.writeInt(hit.chromId);
                out.writeInt(hit.position);
                out.writeInt(hit.mismatches.size());
                for(Mismatch m:hit.mismatches)
                        {
                        out.writeInt(m.position);
                        out.writeByte(m.query);
                        out.writeByte(m.object);
                        }
                }
			}
	
	
	static public class HitDB
		extends DuplicateDB<String, BowtieHit>
		{
		private TupleBinding<String>  keyBinding=new StringBinding();
		private TupleBinding<BowtieHit>  valueBinding=new BowtieHit.Binding();
		
		protected HitDB(Environment env, Transaction txn, String databaseName,
				DatabaseConfig dbConfig) throws DatabaseException
			{
			super(env, txn, databaseName,defaultDbCfg(env, dbConfig));
			}

		@Override
		public TupleBinding<String> getKeyBinding()
			{
			return keyBinding;
			}

		@Override
		public TupleBinding<BowtieHit> getValueBinding()
			{
			return valueBinding;
			}
		
		}
	
	public String readName;
	public byte orientation;
	public int chromId;
    public int position;
    List<Mismatch> mismatches=new ArrayList<Mismatch>(3);
    BowtieHit()
        {

        }
    
    @Override
	public int hashCode()
		{
		final int prime = 31;
		int result = 1;
		result = prime * result + chromId;
		result = prime * result + orientation;
		result = prime * result + position;
		result = prime * result + mismatches.size();
		result = prime * result
				+ ((readName == null) ? 0 : readName.hashCode());
		return result;
		}



	@Override
	public boolean equals(Object obj)
		{
		if (this == obj) return true;
		if (obj == null) return false;
		if (!(obj instanceof BowtieHit)) return false;
		BowtieHit other = (BowtieHit) obj;
		if (chromId != other.chromId) return false;
		if (orientation != other.orientation) return false;
		if (position != other.position) return false;
		if (readName == null)
			{
			if (other.readName != null) return false;
			} else if (!readName.equals(other.readName)) return false;
		return true;
		}



	@Override
    public String toString()
		{
        return "(chr"+chromId+")"+position+"["+(char)orientation+"]"+(mismatches.isEmpty()?"":"mismatches:"+mismatches);
        }

	}
